package e.odbo.data.model;

import com.openbravo.data.basic.BasicException;
import com.openbravo.data.loader.serialize.Datas;
import e.odbo.data.generator.Generator;
import e.odbo.data.generator.I_Generator;
import e.odbo.data.util.ArrayUtils;
import java.util.ArrayList;
import java.util.List;

public class TableData {
    String table;
    String[] column;
    Datas[] types;
    List<Row> datas=new ArrayList<>();

    Migration parent;

    List<GenerateColumn> generateColumns=new ArrayList<>();

    public TableData(String table, String[] column) {
        this.table = table;
        this.column = column;
    }

    public TableData(String table, String[] column,Migration parent) {
        this.table = table;
        this.column = column;
        this.parent=parent;
    }

    public TableData(Table table) {
        this.table = table.getName();
        this.column = table.getColumnNames();
        this.types=table.getColumnDatas();
    }

    protected TableData(String tableName){
        this.table=tableName;
    }


    public static TableData C(String table, String  ...column){
        return new TableData(table,column);
    }

    public static TableData C(String table, String[] column,Migration parent){
        return new TableData(table,column,parent);
    }

    public static TableData C(Table table){
        return new TableData(table);
    }

    public static TableData C(Table table,Migration parent){
        TableData ret=new TableData(table.getName(),table.getColumnNames(),parent);
        ret.types=table.getColumnDatas();
        return ret;
    }

    public static TableData C(Table table,boolean autoAble){
        if(!autoAble)
            return C(table);
        TableData tableData=new TableData(table.getName());
        List<String> base=new ArrayList<>();
        List<String> adv=new ArrayList<>();
        List<Datas> datas=new ArrayList<>();
        for(Column column:table.getColumns()){
            if(null==column.getColumnType().getDefaultGenerator()) {
                if(null!=column.getColumnType().getDefaultValue())
                    tableData.autoValueColumn(column.getName(),column.getColumnType().getDataType(), Generator.DEFAULT(column.getColumnType().getDefaultValue()));
                else {
                    base.add(column.getName());
                    datas.add(column.getColumnType().dataType);
                }
            }else
                tableData.autoValueColumn(column.getName(),column.getColumnType().getDataType(),column.getColumnType().getDefaultGenerator());
        }
        tableData.columns(base.toArray(new String[]{})).types(datas.toArray(new Datas[]{}));
        return tableData;
    }

    public TableData columns(String ...columns){
        this.column=columns;
        return this;
    }

    public TableData types(Datas ...types){
        this.types=types;
        return this;
    }

    public TableData values(Object[]  ...valuesArray){
        for(Object[] data:valuesArray)
            datas.add(Row.C(data));
        return this;
    }

    public TableData value(Object ...values){
        datas.add(Row.C(values));
        return this;
    }

    public <T> TableData autoValueColumn(String column,Datas<T> type,I_Generator<T> generator){
        this.generateColumns.add(new GenerateColumn(column,type,generator));
        return this;
    }

    public <T> TableData autoValueColumn(String column, final ColumnType<T> columnType){
        I_Generator<T> generator=columnType.getDefaultGenerator();
        if(null==generator&&null!=columnType.defaultValue)
            generator= Generator.DEFAULT(columnType.defaultValue);
        if(null==generator)
            generator= Generator.InstanceForClass(columnType.dataType.getClassValue(),8);
        return autoValueColumn(column,columnType.dataType,generator);
    }

    public List<Row> getDatas(){
        return datas;
    }

    public Migration endData(){
        return parent;
    }

    public String getTable() {
        return table;
    }

    public String[] getColumn() {
        return column;
    }

    public Datas[] getTypes(){
        return types;
    }

    public List<GenerateColumn> getGenerateColumns() {
        return generateColumns;
    }

    public void check(Object ...values)throws BasicException{
        if(types==null||types.length!=values.length)
            throw new BasicException("data type check error.type not set or type.length!=values.length");
        for(int i=0;i< types.length;i++) {
            Class typeClass=types[i].getClassValue();
            if (values[i] != null && !typeClass.isInstance(values[i]))
                throw new BasicException("data index:"+i+" check type error,req:"+typeClass.toString()+" ,but input :"+values[i].getClass().toString()+" mock value:"+values[i].toString());
        }
    }

    public String[] getAllColumn() {
        if(generateColumns.isEmpty())
            return column;
        String[] advCol=new String[generateColumns.size()];
        for(int i=0;i<generateColumns.size();i++)
            advCol[i]=generateColumns.get(i).getColumn();
        return ArrayUtils.concatAll(column,advCol);
    }

    public Datas[] getAllTypes() {
        if(generateColumns.isEmpty())
            return types;
        Datas[] advCol=new Datas[generateColumns.size()];
        for(int i=0;i<generateColumns.size();i++)
            advCol[i]=generateColumns.get(i).getType();
        return ArrayUtils.concatAll(types,advCol);
    }

    public Object[] getAllValue(Object[] rowValues) {
        if(generateColumns.isEmpty())
            return rowValues;
        Object[] advCol=new Object[generateColumns.size()];
        for(int i=0;i<generateColumns.size();i++)
            advCol[i]=generateColumns.get(i).generator();
        return ArrayUtils.concatAll(rowValues,advCol);
    }


    public static class GenerateColumn<T>{
        String column;
        Datas<T> type;
        I_Generator<T> generator;

        public GenerateColumn(String column, Datas<T> type,I_Generator<T> generator) {
            this.column = column;
            this.generator = generator;
            this.type=type;
        }

        public T generator(){
            return generator.generator();
        }

        public String getColumn() {
            return column;
        }

        public Datas<T> getType() {
            return type;
        }
    }
}
